﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace Psm.Util
{
    public class AsyncQueue<T> where T:class
    {
        private SpinLock queueLock = new SpinLock();
        private bool processing = false;
        private Queue<T> queue = new Queue<T>();
        private WaitCallback callback;

        public void Enqueue(T item)
        {
            if (item != null)
            {
                bool startPumping = false;
                bool taken = false;
                queueLock.Enter(ref taken);
                //lock(queue)
                {
                    if (!processing)
                    {
                        this.queue.Enqueue(item);
                        startPumping = true;
                        processing = true;
                    }
                    else
                    {
                        this.queue.Enqueue(item);
                        startPumping = false;
                    }
                }
                queueLock.Exit(true);
                if (startPumping)
                    ThreadPool.QueueUserWorkItem((o) => PumpQueue());
            }
        }

        private T Dequeue()
        {  
            return queue.Dequeue();
        }

        public AsyncQueue(WaitCallback processHandler)
        {
            if (processHandler == null) throw new ArgumentNullException("processHandler");
            callback = processHandler;
        }

        public void PumpQueue()
        {
            bool callCallback = false;
            do
            {
                
                bool taken = false;
                T item = null;
                queueLock.Enter(ref taken);
                //lock (queue)
                {
                    if (this.queue.Count > 0)
                    {
                        callCallback = true;
                        item = this.queue.Dequeue();
                    }
                    else
                    {
                        callCallback = processing = false;
                    }
                }
                queueLock.Exit(true);
                if (callCallback)
                    callback(item);
            }
            while (callCallback);
        }
    }
}
